/*
 * $Id: inet.c 142 2011-10-28 07:39:36Z hyunghwan.chung $
 *
   Copyright 2005-2009 Chung, Hyung-Hwan.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an  BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */

/* Copyright (c) 1996-1999 by Internet Software Consortium
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
 * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
 * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
 * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
 * SOFTWARE.
 */

#include <xp/net/inet.h>
#include <xp/bas/endian.h>
#include <xp/bas/ctype.h>
#include <xp/bas/string.h>
#include <xp/bas/stdlib.h>
#include <xp/bas/memory.h>
#include <xp/bas/assert.h>

const xp_ip4addr_t xp_ip4addr_any =
{
	0 /* 0.0.0.0 */
};

const xp_ip4addr_t xp_ip4addr_loopback =
{
#if defined(XP_ENDIAN_BIG)
	0x7F000001u /* 127.0.0.1 */
#elif defined(XP_ENDIAN_LITTLE)
	0x0100007Fu
#else
#	error Unknown endian
#endif
};

const xp_ip6addr_t xp_ip6addr_any =
{
	{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } /* :: */
};

const xp_ip6addr_t xp_ip6addr_loopback =
{
	{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } /* ::1 */
};

int xp_strtoip4addr (const xp_char_t* str, xp_ip4addr_t* ipaddr)
{
	xp_char_t c;
	int dots = 0, digits = 0;
	xp_uint32_t acc = 0, addr = 0;	

	do
	{
		c = *str;	

		if (c == XP_T('\0')) 
		{
			if (dots < 3 || digits == 0) return -1;
			addr = (addr << 8) | acc;
			break;
		}
		else if (c >= XP_T('0') && c <= XP_T('9')) 
		{
			if (digits > 0 && acc == 0) return -1;
			acc = acc * 10 + (c - XP_T('0'));
			if (acc > 255) return -1;
			digits++;
		}
		else if (c == XP_T('.')) 
		{
			if (dots >= 3 || digits == 0) return -1;
			addr = (addr << 8) | acc;
			dots++; acc = 0; digits = 0;
		}
		else return -1;

		str++;
	}
	while (1);

	if (ipaddr != XP_NULL) ipaddr->value = xp_hton32(addr);
	return 0;
}

int xp_strxtoip4addr (
	const xp_char_t* str, xp_size_t len, xp_ip4addr_t* ipaddr)
{
	xp_char_t c;
	int dots = 0, digits = 0;
	xp_uint32_t acc = 0, addr = 0;	
	const xp_char_t* end = str + len;

	do
	{
		if (str >= end) 
		{
			if (dots < 3 || digits == 0) return -1;
			addr = (addr << 8) | acc;
			break;
		}

		c = *str;	

		if (c >= XP_T('0') && c <= XP_T('9'))  
		{
			if (digits > 0 && acc == 0) return -1;
			acc = acc * 10 + (c - XP_T('0'));
			if (acc > 255) return -1;
			digits++;
		}
		else if (c == XP_T('.')) 
		{
			if (dots >= 3 || digits == 0) return -1;
			addr = (addr << 8) | acc;
			dots++; acc = 0; digits = 0;
		}
		else return -1;

		str++;
	} 
	while (1);

	if (ipaddr != XP_NULL) ipaddr->value = xp_hton32(addr);
	return 0;
}

#define __BTOA(b,p,end) \
	do { \
		xp_char_t* sp = p; \
		do {  \
			if (p >= end) { \
				if (p == sp) break; \
				if (p - sp > 1) p[-2] = p[-1]; \
				p[-1] = (b % 10) + XP_T('0'); \
			} \
			else *p++ = (b % 10) + XP_T('0'); \
			b /= 10; \
		} while (b > 0); \
		if (p - sp > 1) { \
			xp_char_t t = sp[0]; \
			sp[0] = p[-1]; \
			p[-1] = t; \
		} \
	} while (0);

#define __ADDDOT(p, end) \
	do { \
		if (p >= end) break; \
		*p++ = XP_T('.'); \
	} while (0)

xp_size_t xp_ip4addrtostrx (const xp_ip4addr_t* ipaddr, xp_char_t* buf, xp_size_t size)
{
	xp_byte_t b;
	xp_char_t* p, * end;
	xp_uint32_t ip;

	if (size <= 0) return 0;

	ip = ipaddr->value;

	p = buf;
	end = buf + size - 1;

#if defined(XP_ENDIAN_BIG)
	b = (ip >> 24) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
	b = (ip >> 16) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
	b = (ip >>  8) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
	b = (ip >>  0) & 0xFF; __BTOA (b, p, end);
#elif defined(XP_ENDIAN_LITTLE)
	b = (ip >>  0) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
	b = (ip >>  8) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
	b = (ip >> 16) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
	b = (ip >> 24) & 0xFF; __BTOA (b, p, end);
#else
	if (xp_is_endian_big()) 
	{
		b = (ip >> 24) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
		b = (ip >> 16) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
		b = (ip >>  8) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
		b = (ip >>  0) & 0xFF; __BTOA (b, p, end);
	}
	else 
	{
		b = (ip >>  0) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
		b = (ip >>  8) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
		b = (ip >> 16) & 0xFF; __BTOA (b, p, end); __ADDDOT (p, end);
		b = (ip >> 24) & 0xFF; __BTOA (b, p, end);
	}
#endif

	*p = XP_T('\0');
	return p - buf;
}

int xp_strtoip6addr (const xp_char_t* src, xp_ip6addr_t* ipaddr)
{
#if 0
	static const xp_char_t xdigits_l[] = XP_T("0123456789abcdef"),
		                  xdigits_u[] = XP_T("0123456789ABCDEF");
	const xp_char-t* xdigits;
#endif

	xp_ip6addr_t tmp;
	xp_byte_t* tp, * endp, * colonp;
	const xp_char_t* curtok;
	xp_char_t ch;
	int saw_xdigit;
	unsigned int val;

	xp_memset (&tmp, 0, XP_SIZEOF(tmp));
	tp = &tmp.value[0];
	endp = &tmp.value[XP_COUNTOF(tmp.value)];
	colonp = XP_NULL;

	/* Leading :: requires some special handling. */
	if (*src == XP_T(':'))
	{
		 if (*++src != XP_T(':')) return -1;
	}

	curtok = src;
	saw_xdigit = 0;
	val = 0;
	while ((ch = *src++) != XP_T('\0')) 
	{
	#if 0
		const char *pch;
		if ((pch = xp_strchr((xdigits = xdigits_l), ch)) == XP_NULL)
			pch = xp_strchr((xdigits = xdigits_u), ch);
		if (pch != XP_NULL) 
		{
			val <<= 4;
			val |= (pch - xdigits);
			if (val > 0xffff) return -1;
			saw_xdigit = 1;
			continue;
		}
	#endif
		int v1;
		if (ch >= XP_T('0') && ch <= XP_T('9'))
			v1 = ch - XP_T('0');
		else if (ch >= XP_T('A') && ch <= XP_T('F'))
			v1 = ch - XP_T('A') + 10;
		else if (ch >= XP_T('a') && ch <= XP_T('f'))
			v1 = ch - XP_T('a') + 10;
		else v1 = -1;
		if (v1 >= 0)
		{
			val <<= 4;
			val |= v1;
			if (val > 0xffff) return -1;
			saw_xdigit = 1;
			continue;
		}

		if (ch == XP_T(':')) 
		{
			curtok = src;
			if (!saw_xdigit) 
			{
				if (colonp) return -1;
				colonp = tp;
				continue;
			}
			else if (*src == XP_T('\0')) 
			{
				/* a colon can't be the last character */
				return -1;
			}

			if (tp + XP_SIZEOF(xp_uint16_t) > endp) return -1;
			*tp++ = (xp_byte_t) (val >> 8) & 0xff;
			*tp++ = (xp_byte_t) val & 0xff;
			saw_xdigit = 0;
			val = 0;
			continue;
		}
	#if 0
		if (ch == XP_T('.') && ((tp + NS_INADDRSZ) <= endp) &&
		    inet_pton4(curtok, tp) > 0) 
		{
			tp += NS_INADDRSZ;
			saw_xdigit = 0;
			break;  /* '\0' was seen by inet_pton4(). */
		}
	#endif
		if (ch == XP_T('.') && ((tp + XP_SIZEOF(xp_ip4addr_t)) <= endp) &&
		    xp_strtoip4addr(curtok, (xp_ip4addr_t*)tp) == 0) 
		{
			tp += XP_SIZEOF(xp_ip4addr_t);
			saw_xdigit = 0;
			break; 
		}

		return -1;
	}

	if (saw_xdigit) 
	{
		if (tp + XP_SIZEOF(xp_uint16_t) > endp) return -1;
		*tp++ = (xp_byte_t) (val >> 8) & 0xff;
		*tp++ = (xp_byte_t) val & 0xff;
	}
	if (colonp != XP_NULL) 
	{
		/*
		 * Since some memmove()'s erroneously fail to handle
		 * overlapping regions, we'll do the shift by hand.
		 */
		xp_size_t n = tp - colonp;
		xp_size_t i;
 
		for (i = 1; i <= n; i++) 
		{
			endp[-i] = colonp[n - i];
			colonp[n - i] = 0;
		}
		tp = endp;
	}

	if (tp != endp) return -1;

	*ipaddr = tmp;
	return 0;
}

int xp_strxtoip6addr (const xp_char_t* src, xp_size_t len, xp_ip6addr_t* ipaddr)
{
	xp_ip6addr_t tmp;
	xp_byte_t* tp, * endp, * colonp;
	const xp_char_t* curtok;
	xp_char_t ch;
	int saw_xdigit;
	unsigned int val;
	const xp_char_t* src_end;

	src_end = src + len;

	xp_memset (&tmp, 0, XP_SIZEOF(tmp));
	tp = &tmp.value[0];
	endp = &tmp.value[XP_COUNTOF(tmp.value)];
	colonp = XP_NULL;

	/* Leading :: requires some special handling. */
	if (src < src_end && *src == XP_T(':'))
	{
		src++;
		if (src >= src_end || *src != XP_T(':')) return -1;
	}

	curtok = src;
	saw_xdigit = 0;
	val = 0;

	while (src < src_end)
	{
		int v1;

		ch = *src++;

		if (ch >= XP_T('0') && ch <= XP_T('9'))
			v1 = ch - XP_T('0');
		else if (ch >= XP_T('A') && ch <= XP_T('F'))
			v1 = ch - XP_T('A') + 10;
		else if (ch >= XP_T('a') && ch <= XP_T('f'))
			v1 = ch - XP_T('a') + 10;
		else v1 = -1;
		if (v1 >= 0)
		{
			val <<= 4;
			val |= v1;
			if (val > 0xffff) return -1;
			saw_xdigit = 1;
			continue;
		}

		if (ch == XP_T(':')) 
		{
			curtok = src;
			if (!saw_xdigit) 
			{
				if (colonp) return -1;
				colonp = tp;
				continue;
			}
			else if (src >= src_end)
			{
				/* a colon can't be the last character */
				return -1;
			}

			*tp++ = (xp_byte_t) (val >> 8) & 0xff;
			*tp++ = (xp_byte_t) val & 0xff;
			saw_xdigit = 0;
			val = 0;
			continue;
		}

		if (ch == XP_T('.') && ((tp + XP_SIZEOF(xp_ip4addr_t)) <= endp) &&
		    xp_strxtoip4addr(curtok, src_end - curtok, (xp_ip4addr_t*)tp) == 0) 
		{
			tp += XP_SIZEOF(xp_ip4addr_t);
			saw_xdigit = 0;
			break; 
		}

		return -1;
	}

	if (saw_xdigit) 
	{
		if (tp + XP_SIZEOF(xp_uint16_t) > endp) return -1;
		*tp++ = (xp_byte_t) (val >> 8) & 0xff;
		*tp++ = (xp_byte_t) val & 0xff;
	}
	if (colonp != XP_NULL) 
	{
		/*
		 * Since some memmove()'s erroneously fail to handle
		 * overlapping regions, we'll do the shift by hand.
		 */
		xp_size_t n = tp - colonp;
		xp_size_t i;
 
		for (i = 1; i <= n; i++) 
		{
			endp[-i] = colonp[n - i];
			colonp[n - i] = 0;
		}
		tp = endp;
	}

	if (tp != endp) return -1;

	*ipaddr = tmp;
	return 0;
}

xp_size_t xp_ip6addrtostrx (const xp_ip6addr_t* ipaddr, xp_char_t* buf, xp_size_t size)
{
	/*
	 * Note that int32_t and int16_t need only be "at least" large enough
	 * to contain a value of the specified size.  On some systems, like
	 * Crays, there is no such thing as an integer variable with 16 bits.
	 * Keep this in mind if you think this function should have been coded
	 * to use pointer overlays.  All the world's not a VAX.
	 */

#define IP6ADDR_NWORDS (XP_SIZEOF(ipaddr->value) / XP_SIZEOF(xp_uint16_t))

	xp_char_t tmp[XP_COUNTOF(XP_T("ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"))], *tp;
	struct { int base, len; } best, cur;
	xp_uint16_t words[IP6ADDR_NWORDS];
	int i;

	if (size <= 0) return 0;

	/*
	 * Preprocess:
	 *	Copy the input (bytewise) array into a wordwise array.
	 *	Find the longest run of 0x00's in src[] for :: shorthanding.
	 */
	xp_memset (words, 0, XP_SIZEOF(words));
	for (i = 0; i < XP_SIZEOF(ipaddr->value); i++)
		words[i / 2] |= (ipaddr->value[i] << ((1 - (i % 2)) << 3));
	best.base = -1;
	cur.base = -1;

	for (i = 0; i < IP6ADDR_NWORDS; i++) 
	{
		if (words[i] == 0) 
		{
			if (cur.base == -1)
			{
				cur.base = i;
				cur.len = 1;
			}
			else
			{
				cur.len++;
			}
		}
		else 
		{
			if (cur.base != -1) 
			{
				if (best.base == -1 || cur.len > best.len) best = cur;
				cur.base = -1;
			}
		}
	}
	if (cur.base != -1) 
	{
		if (best.base == -1 || cur.len > best.len) best = cur;
	}
	if (best.base != -1 && best.len < 2) best.base = -1;

	/*
	 * Format the result.
	 */
	tp = tmp;
	for (i = 0; i < IP6ADDR_NWORDS; i++) 
	{
		/* Are we inside the best run of 0x00's? */
		if (best.base != -1 && i >= best.base &&
		    i < (best.base + best.len)) 
		{
			if (i == best.base) *tp++ = XP_T(':');
			continue;
		}

		/* Are we following an initial run of 0x00s or any real hex? */
		if (i != 0) *tp++ = XP_T(':');

		/* Is this address an encapsulated IPv4? ipv4-compatible or ipv4-mapped */
		if (i == 6 && best.base == 0 &&
		    (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) 
		{
			xp_ip4addr_t ip4addr;
			xp_memcpy (&ip4addr.value, ipaddr->value+12, XP_SIZEOF(ip4addr.value));
			tp += xp_ip4addrtostrx (&ip4addr, tp, XP_COUNTOF(tmp) - (tp - tmp));
			break;
		}

		tp += xp_uint16tostr_lower (words[i], tp, XP_COUNTOF(tmp) - (tp - tmp), 16, XP_T('\0'));
	}

	/* Was it a trailing run of 0x00's? */
	if (best.base != -1 && 
	    (best.base + best.len) == IP6ADDR_NWORDS) *tp++ = XP_T(':');
	*tp++ = XP_T('\0');

	return xp_strxcpy (buf, size, tmp);

#undef IP6ADDR_NWORDS
}

int xp_strtoipaddr (const xp_char_t* str, xp_ipaddr_t* ipaddr)
{
	if (xp_strtoip4addr (str, &ipaddr->u.ip4) <= -1)
	{
		if (xp_strtoip6addr (str, &ipaddr->u.ip6) <= -1) return -1;
		ipaddr->type = XP_IPADDR_IP6;
	}
	else ipaddr->type = XP_IPADDR_IP4;

	return 0;
}

int xp_strxtoipaddr (const xp_char_t* str, xp_size_t len, xp_ipaddr_t* ipaddr)
{
	if (xp_strxtoip4addr (str, len, &ipaddr->u.ip4) <= -1)
	{
		if (xp_strxtoip6addr (str, len, &ipaddr->u.ip6) <= -1) return -1;
		ipaddr->type = XP_IPADDR_IP6;
	}
	else ipaddr->type = XP_IPADDR_IP4;

	return 0;
}

int xp_strtoipaddrx (const xp_char_t* str, xp_ipaddrx_t* ipaddrx)
{
	const xp_char_t* p = str, * ptr;
	xp_ipaddrx_t tmp;
	unsigned int port = 0;

	if (*p == XP_T('['))	
	{
		/* IPv6 address */
		p++; /* skip [ */

		/* collect characters between [ and ] */
		for (ptr = p; *p != XP_T(']'); p++) 
		{
			if (*p == XP_T('\0')) return -1;
		}

		/* parse them into ip6addr */
		if (xp_strxtoip6addr (ptr, p - ptr, &tmp.u.ip6) <= -1) return -1;
		tmp.type = XP_IPADDR_IP6;

		p++; /* skip ] */
	}
	else
	{
		/* IPv4 address */
		for (ptr = p; *p != XP_T(':'); p++)
		{
			if (*p == XP_T('\0'))  return -1;
		}

		if (xp_strxtoip4addr (ptr, p - ptr, &tmp.u.ip4) <= -1) return -1;
		tmp.type = XP_IPADDR_IP4;
	}

	if (*p != XP_T(':')) return -1;
	p++; /* skip : */

	/* port number */
	for (ptr = p; xp_isdigit(*p); p++) 
		port = port * 10 + (*p - XP_T('0'));

	if (*p != XP_T('\0') ||  /* garbage after the port number */
	    p == ptr ||          /* no port number */
	    p - ptr > 5 ||       /* port number too long */
	    port > XP_TYPE_MAX(xp_uint16_t)) return -1;

	tmp.port = xp_hton16(port);

	*ipaddrx = tmp;
	return 0;
}

int xp_strxtoipaddrx (const xp_char_t* str, xp_size_t len, xp_ipaddrx_t* ipaddrx)
{
	const xp_char_t* p = str, * end = str + len, * ptr;
	xp_ipaddrx_t tmp;
	unsigned int port = 0;

	if (*p == XP_T('['))	
	{
		/* IPv6 address */
		p++; /* skip [ */

		for (ptr = p; ; p++)
		{
			if (p >= end) return -1;
			if (*p == XP_T(']')) break;
		}

		if (xp_strxtoip6addr (ptr, p - ptr, &tmp.u.ip6) <= -1) return -1;
		tmp.type = XP_IPADDR_IP6;

		p++; /* skip ] */

		if (p >= end) return -1; /* not to pass over a following : */
	}
	else
	{
		/* IPv4 address */
		for (ptr = p; ; p++)
		{
			if (p >= end) return -1;
			if (*p == XP_T(':')) break;
		}

		if (xp_strxtoip4addr (ptr, p - ptr, &tmp.u.ip4) <= -1) return -1;
		tmp.type = XP_IPADDR_IP4;
	}

	if (*p != XP_T(':')) return -1;
	p++; /* skip : */

	/* port number */
	for (ptr = p; p < end && xp_isdigit(*p); p++) 
		port = port * 10 + (*p - XP_T('0'));

	if (p < end ||      /* garbage after the port number */
	    p == ptr ||     /* no port number */
	    p - ptr > 5 ||  /* port number too long */
	    port > XP_TYPE_MAX(xp_uint16_t)) return -1;

	tmp.port = xp_hton16(port);

	*ipaddrx = tmp;
	return 0;
}

int xp_ipaddrxequal (const xp_ipaddrx_t* a1, const xp_ipaddrx_t* a2)
{
	if (a1->type != a2->type) return 0;
	if (a1->port != a2->port) return 0;

	if (a1->type == XP_IPADDR_IP4)
	{
		return xp_memcmp (&a1->u.ip4.value, &a2->u.ip4.value, XP_SIZEOF(a2->u.ip4.value)) == 0;
	}
	else
	{
		return xp_memcmp (&a1->u.ip6.value, &a2->u.ip6.value, XP_SIZEOF(a2->u.ip6.value)) == 0;
	}
}

/* deprecated */
int xp_inet4_strtoip (const xp_char_t* str, xp_uint32_t* ip)
{
	xp_ip4addr_t ipaddr;
	if (xp_strtoip4addr (str, &ipaddr) <= -1) return -1;
	if (ip) *ip = ipaddr.value;
	return 0;
}

/* deprecated */
int xp_inet4_strxtoip (const xp_char_t* str, xp_size_t len, xp_uint32_t* ip)
{
	xp_ip4addr_t ipaddr;
	if (xp_strxtoip4addr (str, len, &ipaddr) <= -1) return -1;
	if (ip) *ip = ipaddr.value;
	return 0;
}

/* deprecated */
xp_size_t xp_inet4_iptostrx (xp_uint32_t ip, xp_char_t* buf, xp_size_t size)
{
	xp_ip4addr_t ipaddr;
	ipaddr.value = ip;
	return xp_ip4addrtostrx (&ipaddr, buf, size);
}

int xp_inet4_pxtonm (int prefix, xp_uint32_t* netmask)
{
	static xp_uint32_t __nmtab[] =
	{
		0x00000000,
		0x80000000, 0xC0000000, 0xE0000000, 0xF0000000,
		0xF8000000, 0xFC000000, 0xFE000000, 0xFF000000,
		0xFF800000, 0xFFC00000, 0xFFE00000, 0xFFF00000,
		0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,
		0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000,
		0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00,
		0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0, 0xFFFFFFF0,
		0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF
	};

	if (prefix < 0 || prefix > 32) return -1;	
	if (netmask != XP_NULL) *netmask = xp_hton32(__nmtab[prefix]);

	return 0;

	/*
	int p, k;
	xp_uint32_t mask = 0;

	for (k = 24; prefix > 0; k -= 8) {
		p = (prefix >= 8)? 0: (8 - prefix);
		mask |= ((0xFF << p) & 0xFF) << k;
		prefix -= 8;
	}

	if (netmask != XP_NULL) *netmask = xp_hton32(mask);
	return 0;
	*/
}

/*
#define __COUNTUP(x,cnt,rest) \
	switch (x) { \
	case 0xFF000000: cnt += 8; break; \
	case 0x00000000: if (rest) return -1; break; \
	case 0xFE000000: cnt += 7; if (rest) return -1; break; \
	case 0xFC000000: cnt += 6; if (rest) return -1; break; \
	case 0xF8000000: cnt += 5; if (rest) return -1; break; \
	case 0XF0000000: cnt += 4; if (rest) return -1; break; \
	case 0xE0000000: cnt += 3; if (rest) return -1; break; \
	case 0xC0000000: cnt += 2; if (rest) return -1; break; \
	case 0x80000000: cnt += 1; if (rest) return -1; break; \
	default: return -1; \
	}
*/

#define __COUNTUP(x,cnt,rest) \
	switch (x) { \
	case 0xFF000000: \
		cnt += 8; break; \
	case 0x00000000: \
		if (rest) return -1; break; \
	case 0xFE000000: cnt++; \
	case 0xFC000000: cnt++; \
	case 0xF8000000: cnt++; \
	case 0XF0000000: cnt++; \
	case 0xE0000000: cnt++; \
	case 0xC0000000: cnt++; \
	case 0x80000000: cnt++; \
		if (rest) return -1; break; \
	default: return -1; \
	}

int xp_inet4_nmtopx (xp_uint32_t netmask, int* prefix)
{
	int pfx = 0;
	xp_uint32_t x;

	netmask = xp_ntoh32(netmask);

	XP_ASSERT (XP_SIZEOF(netmask) == 4);

	if (netmask) {
		x = netmask & 0xFF000000; netmask <<= 8;
		__COUNTUP (x, pfx, netmask);
	}
	if (netmask) {
		x = netmask & 0xFF000000; netmask <<= 8;
		__COUNTUP (x, pfx, netmask);
	}
	if (netmask) {
		x = netmask & 0xFF000000; netmask <<= 8;
		__COUNTUP (x, pfx, netmask);
	}
	if (netmask) {
		x = netmask & 0xFF000000; 
		__COUNTUP (x, pfx, 0);
	}

	if (prefix != XP_NULL) *prefix = pfx;
	return 0;
}

int xp_inet4_strtoipnm (
	const xp_char_t* str, xp_uint32_t* ip, xp_uint32_t* mask)
{
	const xp_char_t* slash, * p;
	xp_uint32_t tip, tmask;

	slash = xp_strchr (str, XP_T('/'));
	if (slash == XP_NULL) {
		if (xp_inet4_strtoip(str, &tip) == -1) return -1;
		tmask = XP_TYPE_MAX(xp_uint32_t);
	}
	else {
		if (xp_inet4_strxtoip(str, slash - str, &tip) == -1) return -1;

		if (xp_strchr(slash + 1, XP_T('.')) == XP_NULL) {
			int prefix = 0;

			p = slash + 1;
			if (*p == XP_T('\0')) return -1;
			while (xp_isdigit(*p)) {
				prefix = prefix * 10 + (*p - XP_T('0'));
				if (prefix > 32) return -1;
				p++;
			}
			if (*p != XP_T('\0')) return -1;
			xp_inet4_pxtonm (prefix, &tmask);
		}
		else {
			if (xp_inet4_strtoip(slash + 1, &tmask) == -1) return -1;
		}
	}

	if (ip != XP_NULL) *ip = tip;
	if (mask != XP_NULL) *mask = tmask;
	return 0;
}

int xp_inet4_strxtoipnm (
	const xp_char_t* str, xp_size_t len, xp_uint32_t* ip, xp_uint32_t* mask)
{
	const xp_char_t* slash, * p;
	const xp_char_t* end = str + len;
	xp_uint32_t tip, tmask;

	slash = xp_strxchr (str, len, XP_T('/'));
	if (slash == XP_NULL) {
		if (xp_inet4_strxtoip(str, len, &tip) == -1) return -1;
		tmask = XP_TYPE_MAX(xp_uint32_t);
	}
	else {
		if (xp_inet4_strxtoip(str, slash - str, &tip) == -1) return -1;

		if (xp_strxchr(slash + 1, end - slash - 1, XP_T('.')) == XP_NULL) {
			int prefix = 0;

			p = slash + 1;
			if (p >= end) return -1;
			while (p < end && xp_isdigit(*p)) {
				prefix = prefix * 10 + (*p - XP_T('0'));
				if (prefix > 32) return -1;
				p++;
			}
			if (p < end) return -1;

			xp_inet4_pxtonm (prefix, &tmask);
		}
		else {
			if (xp_inet4_strxtoip(slash + 1, end - slash - 1, &tmask) == -1) return -1;
		}
	}

	if (ip != XP_NULL) *ip = tip;
	if (mask != XP_NULL) *mask = tmask;
	return 0;
}

xp_size_t xp_inet4_ipnmtostrx (
	xp_uint32_t ip, xp_uint32_t mask, xp_char_t* buf, xp_size_t size)
{
	xp_size_t len;
	
	len = xp_inet4_iptostrx(ip, buf, size);
	buf += len; size -= len;
	if (size > 1) {
		*buf++ = XP_T('/'); size--; len++;
		*buf = XP_T('\0');

		if (size > 1) {
			len += xp_inet4_iptostrx (mask, buf, size);
		}
	}

	return len;
}

xp_size_t xp_inet4_ipnmtostrpx (
	xp_uint32_t ip, xp_uint32_t mask, xp_char_t* buf, xp_size_t size)
{
	xp_char_t* p = buf;
	xp_char_t* end = p + size - 1;
	
	p += xp_inet4_iptostrx(ip, p, size);
	if (p < end) {
		*p++ = XP_T('/'); 
		*p = XP_T('\0');

		if (p < end) {
			int prefix;

			if (xp_inet4_nmtopx(mask, &prefix) == -1) {
				return (xp_size_t)-1;
			}

			XP_ASSERT (prefix <= XP_TYPE_MAX(xp_byte_t));
			__BTOA (prefix, p, end - 1); *p = XP_T('\0');
		}
	}

	return p - buf;
}

#if 0

#define XP_INET_HOST_MAX NI_MAXHOST
#define XP_INET_SERVICE_MAX NI_MAXSERV

/*
 * If host is "", it is resolved to localhost.
 * If host is XP_NULL, it is resolved to INADDR_ANY or IN6ADDR_ANY_INIT.
 */
int xp_inet_resolve (
	const xp_char_t* host, const xp_char_t* service,
	xp_port_t* port, xp_ip_t* ip)
{
	struct addrinfo hints;
	struct addrinfo* info, * p;
#ifndef XP_CHAR_IS_MCHAR
	xp_mchar_t mb_host[XP_INET_HOST_MAX + 1];
	xp_mchar_t mb_service[XP_INET_SERVICE_MAX + 1];
	xp_mchar_t* p_host = XP_NULL;
	xp_mchar_t* p_service = XP_NULL;
#endif

	xp_memset (&hints, 0, XP_SIZEOF(hints));

	hints.ai_family = family;
	hints.ai_socktype = type;

	if (host == XP_NULL) hints.ai_flags = AI_PASSIVE;
	else if (hosts[0] == XP_T('\0')) host = XP_NULL;

#ifdef XP_CHAR_IS_MCHAR
	n = getaddrinfo (host, service, &hints, &info);
#else
	if (host != XP_NULL) {
		if (xp_wcstomcs (host, 
			mb_host, xp_countof(mb_host)) == 0) {
			*errnum = EAI_NONAME;
			return -1;
		}
		p_host = mb_host;
	}
	if (service != XP_NULL) {
		if (xp_wcstomcs (service, 
			mb_service, xp_countof(mb_service)) == 0) {
			*errnum = EAI_NONAME;
			return -1;
		}
		p_service = mb_service;
	}
	n = getaddrinfo (p_host, p_service, &hints, &info);	
#endif
	if (n != 0) return -1;

	for (p = info; p != XP_NULL; p = p->ai_next) {
		if (family != V0 && p->ai_family != family) continue;
		if (type != 0 && p->ai_socktype != type) continue;
		if (XP_SIZEOF(address_storage) < p->ai_addrlen) continue;

		xp_memcpy (&address_storage, p->ai_addr, p->ai_addrlen);
		break;
	}

	freeaddrinfo (info);
	return 0;
}

#endif

